/**
 * @description  : 上传组件
 * @descriptionDetail : 上传组件
 * @copyright    : 浙江烟草
 * @author       : mc
 * @create       : 2022-10-03 17:23:27
 */
<template>
  <view class="m-upload">
    <view
      class="m-list-item m-preview-wrap"
      :style="{
        width: $u.addUnit(width),
        height: $u.addUnit(height)
      }"
      v-for="(item, index) in lists"
      :key="index"
    >
      <view
        class="m-delete-icon"
        :style="{
          background: delBgColor
        }"
        v-if="deletable"
        @tap.stop="deleteItem(index)"
      >
        <u-icon class="m-icon" :name="delIcon" size="20" :color="delColor"></u-icon>
      </view>
      <image
        class="m-preview-image"
        :src="getAkAppendImg(item)"
        :mode="imageMode"
        v-if="!item.isImage"
        @tap.stop="doPreviewImage(item, index)"
      ></image>
    </view>
    <view style="display: inline-block;" v-if="!disabled && maxCount > lists.length" @tap="selectFile">
      <slot name="addBtn"></slot>
      <view
        class="m-list-item m-add-wrap"
        hover-class="m-add-wrap__hover"
        hover-stay-time="150"
        :style="{
          width: $u.addUnit(width),
          height: $u.addUnit(height)
        }"
        v-if="!customBtn"
      >
        <u-icon name="plus" class="m-add-btn" size="40" v-if="showDefView"></u-icon>
        <view class="m-add-tips">
          {{ uploadText }}
        </view>
      </view>
    </view>
  </view>
</template>

<script>
/**
	 * upload 图片上传
	 * @description 该组件用于上传图片场景
	 * @tutorial https://www.uviewui.com/components/upload.html
	 * @property {String Number} max-count 最大选择图片的数量（默认99）
	 * @property {Boolean} custom-btn 如果需要自定义选择图片的按钮，设置为true（默认false）
	 * @property {Boolean} disabled 是否启用(显示/移仓)组件（默认false）
	 * @property {String} image-mode 预览图片等显示模式，可选值为uni的image的mode属性值（默认aspectFill）
	 * @property {String} del-icon 右上角删除图标名称，只能为uView内置图标
	 * @property {String} del-bg-color 右上角关闭按钮的背景颜色
	 * @property {String | Number} index 在各个回调事件中的最后一个参数返回，用于区别是哪一个组件的事件
	 * @property {String} del-color 右上角关闭按钮图标的颜色
	 * @property {String} name 上传文件的字段名，供后端获取使用（默认file）
	 * @property {Array<String>} size-type original 原图，compressed 压缩图，默认二者都有（默认['original', 'compressed']）
	 * @property {Array<String>} source-type 选择图片的来源，album-从相册选图，camera-使用相机，默认二者都有（默认['album', 'camera']）
	 * @property {Boolean} preview-full-image	是否可以通过uni.previewImage预览已选择的图片（默认true）
	 * @property {Boolean} multiple	是否开启图片多选，部分安卓机型不支持（默认true）
	 * @property {Boolean} deletable 是否显示删除图片的按钮（默认true）
	 * @property {Array<Object>} file-list 默认显示的图片列表，数组元素为对象，必须提供url属性
	 * @property {Boolean} upload-text 选择图片按钮的提示文字（默认“选择图片”）
	 * @event {Function} on-preview 全屏预览图片时触发
	 * @event {Function} on-remove 移除图片时触发
	 * @event {Function} on-choose-complete 每次选择图片后触发，只是让外部可以得知每次选择后，内部的文件列表
	 * @example <u-upload :action="action" :file-list="fileList" ></u-upload>
	 */
import {
  base64ToPath,
} from 'image-tools';
import {
  PATHROOT,
} from '@/common/path.config';
import commonService from '@/service/common/common.service';
import {dataURLtoFile,} from '@/common/util.js';
export default {
  name: 'MUpload',
  props: {
    // 最大上传数量
    maxCount: {
      type: [String, Number,],
      default: 10,
    },
    // 上传文件其他参数
    uploadParam: {
      type: Object,
      default: ()=>{return {};},
    },
    upApiUrl: {
      type: String,
      default: "uploadBase64ForApp", // 可选 uploadBase64File , fileUpload  仓库用
    },
    // 是否启用
    disabled: {
      type: Boolean,
      default: false,
    },
    // 预览上传的图片时的裁剪模式，和image组件mode属性一致
    imageMode: {
      type: String,
      default: 'aspectFill',
    },
    sourceType: {
      type: Number,
      default: 0, // 0 全部  1 album  2 camera
    },
    // 是否在点击预览图后展示全屏图片预览
    previewFullImage: {
      type: Boolean,
      default: true,
    },
    // 是否开启图片多选，部分安卓机型不支持
    multiple: {
      type: Boolean,
      default: true,
    },
    // 是否展示删除按钮
    deletable: {
      type: Boolean,
      default: true,
    },
    // 显示已上传的文件列表
    fileList: {
      type: Array,
      default () {
        return [];
      },
    },
    // 上传区域的提示文字
    uploadText: {
      type: String,
      default: '选择图片',
    },
    // 是否通过slot自定义传入选择图标的按钮
    customBtn: {
      type: Boolean,
      default: false,
    },
    // 内部预览图片区域和选择图片按钮的区域宽度
    width: {
      type: [String, Number,],
      default: 200,
    },
    showDefView: {
      type: Boolean,
      default: true,
    },
    // 内部预览图片区域和选择图片按钮的区域高度
    height: {
      type: [String, Number,],
      default: 200,
    },
    // 右上角关闭按钮的背景颜色
    delBgColor: {
      type: String,
      default: '#fa3534',
    },
    // 右上角关闭按钮的叉号图标的颜色
    delColor: {
      type: String,
      default: '#ffffff',
    },
    // 右上角删除图标名称，只能为uView内置图标
    delIcon: {
      type: String,
      default: 'close',
    },
    // 允许上传的图片后缀
    limitType: {
      type: Array,
      default () {
        // 支付宝小程序真机选择图片的后缀为"image"
        // https://opendocs.alipay.com/mini/api/media-image
        return ['png', 'jpg', 'jpeg', 'webp', 'gif', 'image',];
      },
    },
    // 在各个回调事件中的最后一个参数返回，用于区别是哪一个组件的事件
    index: {
      type: [Number, String,],
      default: '',
    },
	canUploadFun: { // 自定义校验是否可以上传图片
	  type: Function,
	  default: (t) => {return true;}
	}
  },
  data() {
    return {
      lists: [],
      user: this.$store.state.userInfo,
      fileViewUrl: PATHROOT.BASE + "/commonfile/filestore/downloadFile.do",
    };
  },
  watch: {
    fileList: {
      immediate: true,
      handler(val) {
        val.map(value => {
          // 首先检查内部是否已经添加过这张图片，因为外部绑定了一个对象给fileList的话(对象引用)，进行修改外部fileList
          // 时，会触发watch，导致重新把原来的图片再次添加到this.lists
          // 数组的some方法意思是，只要数组元素有任意一个元素条件符合，就返回true，而另一个数组的every方法的意思是数组所有元素都符合条件才返回true
          let ptmp = value;
          if(this.upApiUrl == 'fileUpload'){
            ptmp.gfUrl = ptmp.url;
            ptmp.gfiId = ptmp.id;
          }
          let tmp = this.lists.some(val => {
            return val.gfiId == ptmp.gfiId;
          })
          // 如果内部没有这个图片(tmp为false)，则添加到内部
          !tmp && this.lists.push(ptmp);
        });
      },
    },
    // 监听lists的变化，发出事件
    lists(n) {

      this.$emit('on-list-change', n, this.index);
    },
  },
  mounted() {
    this.fileViewUrl += "?tk=" + this.user.tk + "&fileId=";
  },
  methods: {
    // 清除列表
    clear() {
      this.lists = [];
    },
    // 选择图片
    selectFile() {
      if (this.disabled) return;
	  if(!this.canUploadFun(this.index)) return;
      const {
        name = '', maxCount, multiple, maxSize, sizeType, lists, camera, compressed, maxDuration, sourceType,
      } = this;
      let chooseFile = null;
      const newMaxCount = maxCount - lists.length;
      let self = this;
      // 设置为只选择图片的时候使用 chooseImage 来实现
      chooseFile = new Promise((resolve, reject) => {
        self.$deviceApi.chooseImageToCompress({
          maxSize: multiple ? (newMaxCount > 9 ? 9 : newMaxCount) : 1,
          sType: sourceType,
          quality: 0.65,
          widthScale: 0.7,
          backType: 1,
        }).then(res => {
          resolve(res);
        }).catch(err => {
          reject(err);
        });
      });
      chooseFile
        .then(res => {
          let file = null;
          let listOldLength = this.lists.length;
          let promiseArr = [];
          res.map((val, index) => {
            // 如果是非多选，index大于等于1或者超出最大限制数量时，不处理
            if (!multiple && index >= 1) return;
            if (maxCount <= lists.length) {
              this.$emit('on-exceed', val, this.lists, this.index);
              this.showToast('超出最大允许的文件个数');
              return;
            }
            promiseArr.push(self.uploadFile(val, Date.now() + "_" + index + ".jpg"));
          });
          Promise.all(promiseArr).then(res => {
            res.forEach(itm => {
              self.lists.push(itm);
            });
            // 每次图片选择完，抛出一个事件，并将当前内部选择的图片数组抛出去
            this.$emit('on-choose-complete', self.lists, this.index);
          }).catch(err => {
            this.$modal.alert('上传失败');
            this.$emit('on-choose-fail', err);
          });
        })
        .catch(error => {
          this.$emit('on-choose-fail', error);
        });
    },
    uploadFile(base64, fileName) {
      let self = this;
      self.uploadParam['fileName'] = fileName;
      return new Promise((resolve, reject) => {
        if (self.upApiUrl == 'uploadBase64ForApp') {
          commonService.uploadBase64ForApp(base64, self.uploadParam).then(res => {
            if (res.success) {
              resolve(res.bean);
            } else {
              reject('上传失败');
            }
          });
        }else if(self.upApiUrl == 'fileUpload'){
          let file = dataURLtoFile(base64,fileName);
          commonService.uploadFile(file, self.uploadParam.attrCode).then(res => {
            if (res.success) {
              res.bean.gfUrl = res.bean.url;
              res.bean.gfiId = res.bean.id;
              resolve(res.bean); 
            } else {
              reject('上传失败');
            }
          });
        } else {
          self.uploadParam.base64Data = base64;
          commonService.uploadBase64File(self.uploadParam).then(res => {
            if (res.success) {
              resolve(res.bean);
            } else {
              reject('上传失败');
            }
          });
        }
      });
    },
    // 提示用户消息
    showToast(message, force = false) {
      if (this.showTips || force) {
        this.$modal.toast(message);
      }
    },
    // 删除一个图片
    deleteItem(index) {
      let self = this;
      this.$modal.alert('您确定要删除此项吗？', '提示', () => {
        if(this.upApiUrl == 'fileUpload'){
          commonService.delFile(self.lists[index].gfiId).then(res => {
            if (res.success) {
              self.handlerDeleteItem(index);
            } else {
              self.$modal.toast('删除失败');
            }
          }).catch(err => {
            self.$modal.toast('删除失败');
          });
        }else{
          commonService.delFile(self.lists[index].gfiId).then(res => {
            if (res.success) {
              self.handlerDeleteItem(index);
            } else {
              self.$modal.toast('删除失败');
            }
          }).catch(err => {
            self.$modal.toast('删除失败');
          });
        }
      }, '',true);
    },
    getAkAppendImg(item) {
      console.log('数据' + JSON.stringify(item))
      if (item.gfUrl) {
        return item.gfUrl;
      }
      return this.fileViewUrl + item.gfiId;
    },
    // 执行移除图片的动作，上方代码只是判断是否可以移除
    handlerDeleteItem(index) {
      this.lists.splice(index, 1);
      this.$forceUpdate();
      this.$emit('on-remove', index, this.lists, this.index);
      // this.showToast('移除成功');
    },
    // 用户通过ref手动的形式，移除一张图片
    remove(index) {
      // 判断索引的合法范围
      if (index >= 0 && index < this.lists.length) {
        this.lists.splice(index, 1);
        this.$emit('on-list-change', this.lists, this.index);
      }
    },
    getData() {
      return this.lists;
    },
    // 预览图片
    doPreviewImage(itm, index) {
      if (!this.previewFullImage) return;
      let totalUrlList = [itm, ...this.lists,]; // 数组第一个是 current, 其他数组内容作为 urls
      this.$u.loading("预览图片加载中");
      Promise.all(totalUrlList.map(tUrl => {
        return this.base64ToPathOrToken(tUrl);
      })).then(res => {
        this.$u.hideLoading();
        this.viewPhoto(res);
      });
    },
    // base64转换地址，或者地址增加token
    base64ToPathOrToken(item) {
      return new Promise((resolve, reject) => {
        resolve(this.getAkAppendImg(item));
      });
    },
    viewPhoto(totalUrlList) {
      let currUrl = totalUrlList[0];
      let images = totalUrlList.splice(0, 1);
      this.$deviceApi.previewImage(images, currUrl);
    },
  },
};
</script>

<style lang="scss" scoped>
	.m-upload {
		display: flex;
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;
	}

	.m-list-item {
		width: 200rpx;
		height: 200rpx;
		overflow: hidden;
		margin: 10rpx;
		background: rgb(244, 245, 246);
		position: relative;
		border-radius: 10rpx;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		align-items: center;
		justify-content: center;
	}

	.m-preview-wrap {
		border: 1px solid rgb(235, 236, 238);
	}

	.m-add-wrap {
		flex-direction: column;
		color: $u-content-color;
		font-size: 26rpx;
	}

	.m-add-tips {
		margin-top: 20rpx;
		line-height: 40rpx;
	}

	.m-add-wrap__hover {
		background-color: rgb(235, 236, 238);
	}

	.m-preview-image {
		display: block;
		width: 100%;
		height: 100%;
		border-radius: 10rpx;
	}

	.m-delete-icon {
		position: absolute;
		top: 10rpx;
		right: 10rpx;
		z-index: 10;
		background-color: $u-type-error;
		border-radius: 100rpx;
		width: 44rpx;
		height: 44rpx;
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}

	.m-icon {
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}

	.m-progress {
		position: absolute;
		bottom: 10rpx;
		left: 8rpx;
		right: 8rpx;
		z-index: 9;
		width: auto;
	}

	.m-error-btn {
		color: #ffffff;
		background-color: $u-type-error;
		font-size: 20rpx;
		padding: 4px 0;
		text-align: center;
		position: absolute;
		bottom: 0;
		left: 0;
		right: 0;
		z-index: 9;
		line-height: 1;
	}
</style>
